#include "umSource.h"
#include "umSink.h"
#include "umlog.h"
#include "umapi_socket.h"
#define TARGET "AVSOUTCE"

typedef struct
{
	S_SOURCE *source;
	UM_HANDLE mutexHandle, taskHandle,taskHandle1, fd,fffd;
	UM_U16 localPort;
	UM_S8 flag, stopflag;
	AVFormatContext *in;
	struct umapi_list_head sinks;
}S_AVCONTEXT;
static UM_VOID sendDatas(UM_VOID *pSource, AVPacket *pkt)
{
	S_AVCONTEXT *pAv = (S_AVCONTEXT*)pSource;
	umapi_os_mutex_unlock(pAv->mutexHandle);
	if(pAv->flag)
	{
		struct umapi_list_head *pos, *n;
		umapi_list_for_each_safe(pos, n, &pAv->sinks)
		{
			S_SINK *sink = umapi_list_entry(pos,S_SINK, head);
			if(sink->writePacket)
			{
				sink->writePacket(sink, pkt);
			}
		}
	}
	umapi_os_mutex_unlock(pAv->mutexHandle);
	return ;
}
static UM_VOID attachSink(S_AVCONTEXT *pAv)
{
	umapi_os_mutex_unlock(pAv->mutexHandle);
	if(pAv->flag)
	{
		struct umapi_list_head *pos, *n;
		umapi_list_for_each_safe(pos, n, &pAv->sinks)
		{
			S_SINK *sink = umapi_list_entry(pos,S_SINK, head);
			if(sink->attachSource)
			{
				sink->attachSource(sink, pAv->source);
			}
		}
	}
	umapi_os_mutex_unlock(pAv->mutexHandle);
}
static UM_S32 getSampleRateIndex(UM_U32 sampleRate)
{
    switch(sampleRate)
    {
        case 96000:
            return 0x0;
        case 88200:
            return 0x1;
        case 64000:
            return 0x2;
        case 48000:
            return 0x3;
        case 44100:
            return 0x4;
        case 32000:
            return 0x5;
        case 24000:
            return 0x6;
        case 22050:
            return 0x7;
        case 16000:
            return 0x8;
        case 12000:
            return 0x9;
        case 11025:
            return 0xa;
        case 8000:
            return 0xb;
        case 7350:
            return 0xc;
        default:
            return 0xf;
    }
}

static UM_U32 getChannelCnt(uint64_t channel_layout)
{
    switch(channel_layout)
    {
        case AV_CH_LAYOUT_MONO:
            return 1;
        case AV_CH_LAYOUT_STEREO:
            return 2;
        default:
            DEBUG("Soundmode can not support!channel_layout=0x%x",(uint32_t)channel_layout);
            return 1;
    }
    return 1;
}

static UM_VOID handoutTask(UM_VOID *param)
{
	S_AVCONTEXT *pAv = (S_AVCONTEXT*)param;
	UM_CHAR srcBuff[200];
	umapi_sprintf(srcBuff, "tcp://127.0.0.1:%d", pAv->localPort);
	LOGI("open avformat:%s", srcBuff);
	if(avformat_open_input(&pAv->in, srcBuff, UM_NULL, UM_NULL) <0)
	{
		LOGE("open avformart:%s Fail", srcBuff);
		goto CREATEFAIL0;
	}
	//	pAv->in->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;

	if(avformat_find_stream_info(pAv->in, UM_NULL) < 0)
	{
		LOGE("find stream:%s Fail", srcBuff);
		goto CREATEFAIL1;
	}
	
	UM_S32 i;
	for(i =0 ;i <pAv->in->nb_streams;i++)
	{
		if(pAv->in->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
		{
			if(pAv->in->streams[i]->codecpar->codec_id == AV_CODEC_ID_AAC)
			{
				UM_S32 profile = pAv->in->streams[i]->codecpar->profile; // AAC LC
			    UM_S32 freqIdx = getSampleRateIndex(pAv->in->streams[i]->codecpar->sample_rate); // 16KHz
			    UM_S32 chanCfg = getChannelCnt(pAv->in->streams[i]->codecpar->channels); // CPE
				pAv->in->streams[i]->codecpar->extradata = av_mallocz(32);
			    // fill in ADTS data
			    pAv->in->streams[i]->codecpar->extradata[0] = 0xFF;
			    pAv->in->streams[i]->codecpar->extradata[1] = 0xF1;
			    pAv->in->streams[i]->codecpar->extradata[2] = (((profile - 1) << 6) + (freqIdx << 2) + (chanCfg >> 2));
			    pAv->in->streams[i]->codecpar->extradata[3] = (((chanCfg & 3) << 6) + ((7 & 0x1800) >> 11));
			    pAv->in->streams[i]->codecpar->extradata[4] = ((7 & 0x7f8 )>> 3) & 0xff;
			    pAv->in->streams[i]->codecpar->extradata[5] = ((7 & 0x7)<<5) | 0x1f;
			    pAv->in->streams[i]->codecpar->extradata[6] = 0xFC;
			    pAv->in->streams[i]->codecpar->extradata_size = 7;
				pAv->in->streams[i]->codec->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
			}
		}
	}
	
	av_dump_format(pAv->in, 0 ,srcBuff, 0);
	attachSink(pAv);
	UM_S32 s32ret ;
	while(pAv->flag)
	{
		AVPacket pkt;
		s32ret = av_read_frame(pAv->in, &pkt);
		if(s32ret <0 )
		{
			break;
		}
		sendDatas(pAv, &pkt);
		av_packet_unref(&pkt);
	}
CREATEFAIL1:
	avformat_close_input(&pAv->in);
CREATEFAIL0:
	avformat_free_context(pAv->in);	
	pAv->stopflag = 1;
	return;
}
static UM_VOID listenFFMpegTask(UM_VOID *param)
{
	S_AVCONTEXT *pAv = (S_AVCONTEXT*)param;
	UM_HANDLE fd;
	UMAPI_SOCKET_ADDR ipaddr;
	while(pAv->flag)
	{
		fd = sink_accept_timed(pAv->fd, &ipaddr, 1000);		
		if(fd < 0)
			continue;
		LOGI("fd:%d, %s:%d" ,fd,
			umapi_socket_inet_ntoa(ipaddr.addr.ipv4), ipaddr.sin_port);
		pAv->fffd = fd;
	}
}

static UM_S32 sendDataToSinks(UM_VOID *pSource, AVPacket *pkt)
{
	S_AVCONTEXT *pAv = GETCONTEXT(S_AVCONTEXT *, pSource);
	if(pAv->fffd != 0)
	{
		if( umapi_socket_send(pAv->fffd, pkt->data, pkt->size, 0) <0 )
		{
			umapi_socket_close(pAv->fffd);
			pAv->fffd = 0;
		}
	}
}

static UM_S32 init(S_AVCONTEXT *pAv, S_SOURCE *pSource)
{
	LOGI("init Av Source %p", pAv);
	pAv->source = pSource;
	umapi_os_mutex_create(UM_NULL,UM_OS_MUTEX_ATTR_TIMED_NP,&pAv->mutexHandle);
	umapi_list_create(&pAv->sinks);
	pAv->fd = umapi_socket_open(UMAPI_AF_INET,UMAPI_SOCK_STREAM,0);
	UMAPI_SOCKET_ADDR localaddr;
	localaddr.addr.ipv4 = 0;
	localaddr.sin_port = 0;
	localaddr.sa_family = UMAPI_AF_INET;
	UM_S32 s32ret = umapi_socket_bind(pAv->fd, &localaddr);
	s32ret |= umapi_socket_listen(pAv->fd,1);
	s32ret |= umapi_socket_getsockname(pAv->fd, &localaddr);
	pAv->localPort = localaddr.sin_port;
	pAv->flag = 1;
	pAv->stopflag = 0;
	pAv->fffd = 0;
	LOGI("av fd:%d, port:%d",pAv->fd, pAv->localPort);
	umapi_os_task_create(UM_NULL,listenFFMpegTask, pAv,8,0, &pAv->taskHandle);
	umapi_os_task_create(UM_NULL,handoutTask, pAv,8,0, &pAv->taskHandle1);
	return UM_SUCCESS;
}

static UM_VOID deinit(S_AVCONTEXT *pAv)
{
	LOGI("deinit Av Source %p", pAv);
	pAv->flag = UM_FALSE;
	while(!pAv->stopflag)
	{
		umapi_os_task_sleep(100);
	}
	LOGI("wait for AvSource stop ");
	if(pAv->fffd)
		umapi_socket_close(pAv->fffd);
	umapi_socket_close(pAv->fd);
}
static AVFormatContext *getAvIn(UM_VOID *pSource)
{
	S_AVCONTEXT *p = GETCONTEXT(S_AVCONTEXT *, pSource);
	return p->in;
}

BASICSOURCEFUN(S_AVCONTEXT, init, deinit);


S_SOURCE avSource = 
{
    .protocol = "av",
	.describe = "avStream==>avPacket",
    .open = open,
    .open2 = open2,
    .addSink = addSink,
    .delSink = delSink,
    .start =  start,
    .stop = stop,
    .destroy = destroy,
    .getAvIn = getAvIn,
    .priSize = sizeof(S_AVCONTEXT),
};

